// 判断对象是否是对象
const isObject = val => val !== null && typeof val === 'object'
const convent = target => isObject(target) ? reactive(target) : target
const hasOwn = (target, key) => Object.prototype.hasOwnProperty.call(target, key)
export function reactive(target) {
  // 如果不是对象的话就直接返回
  if(!isObject(target)) return target
  
  const handler = {
    get(target, key, receiver) {
      // 收集依赖
      track(target, key)
      const result = Reflect.get(target, key, receiver)
      return convent(result)
    },
    // set 方法需要返回一个布尔类型的值，表示赋值是否成功
    set(target, key, value, receiver) {
      // 获取旧值的目的是为了和新值比对是否相等，如果相等就不需要任何处理
      // 如果不相等 就要调用Reflect.set 方法去重新处理这个值
      // 并且需要重新触发更新
      const oldValue = Reflect.get(target, key, receiver)
      let result = true
      if(oldValue !== value) {
        result = Reflect.set(target, key, value, receiver)
        // 触发更新
        trigger(target, key)
      }
      return result
    },
    // 我们需要先判断 target 中是否有自己的 key属性
    // 如果有 key 属性， 删除成功之后，需要通知更新
    deleteProperty(target, key) {
      const hadKey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
      if(hadKey && result){
        // 触发更新
        trigger(target, key)
      }
      return result
    }
  }
  return new Proxy(target, handler)
}

let activeEffect = null 
export function effect(callback) {
  activeEffect = callback // 访问响应式对象属性，去收集依赖 
  callback()
  // - 为什么还需要重新设置`activeEffect = null`？
  // - 因为收集依赖的时候，如果有依赖的话，是一个递归的过程
  activeEffect = null
}

let targetMap = new WeakMap()
export function track(target, key) {
  if(!activeEffect) return
  let depsMap = targetMap.get(target)
  if(!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if(!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  dep.add(activeEffect)
}

export function trigger(target, key) {
  // 我们需要根据 target  从 targetMap 中 找到 depsMap
  const depsMap = targetMap.get(target)
  if(!depsMap) return
  // 根据key找对应的 dep集合
  const dep = depsMap.get(key)
  // dep集合中存储的是 对应key存储的 effect 函数
  if(dep){
    dep.forEach(effect => {
      effect()
    });
  }
}

export function ref(raw) {
  // 判断 raw 是否是 ref 创建的对象，如果是的话直接返回
  if(isObject(raw) && raw.__v_isRef){
    return
  }
  let value = convert(raw)
  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value') // 收集依赖
      return value
    },
    set value(newValue) {
      if(newValue !== value){
        raw = newValue
        value = convert(raw) 
        track(r, 'value') // 收集依赖
      }
    }
  }
  return r
}

export function toRefs(proxy) {
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}

  for(const key in proxy) {
    ret[key] = toProxyRef(proxy, key)
  }
}
function toProxyRef (proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value')
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  return r
}